from typing import Union, Annotated

from fastapi import APIRouter, Depends, Cookie, HTTPException, Header

router = APIRouter()


# 依赖项 ：依赖项应该是 "可调用对象"，包括函数、可调用对象 等
# ==================== 函数依赖项 ====================
# 在普通的 def 路径操作函数中，可以声明异步的 async def 依赖项；
# 也可以在异步的 async def 路径操作函数中声明普通的 def 依赖项
async def common_parameters(q: Union[str, None] = None, skip: int = 0, limit: int = 100):
    print("--- 进入依赖项")
    return {"q": q, "skip": skip, "limit": limit}


@router.get("/depend/items_1/", tags=["依赖项"])
async def read_items_1(commons: dict = Depends(common_parameters)):
    print("--- 进入路由")
    return commons


@router.get("/depend/users_2/", tags=["依赖项"])
def read_users_2(commons: dict = Depends(common_parameters)):
    return commons


#  ==================== 类作为依赖项 ====================
class CommonQueryParams:
    def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


@router.get("/depend/items_3/", tags=["依赖项"])
async def read_items_3(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip: commons.skip + commons.limit]
    response.update({"items": items})
    return response


@router.get("/depend/items_4/", tags=["依赖项"])
async def read_items_4(commons=Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip: commons.skip + commons.limit]
    response.update({"items": items})
    return response


# 这种方式最好
@router.get("/depend/items_5/", tags=["依赖项"])
async def read_items_5(commons: CommonQueryParams = Depends()):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip: commons.skip + commons.limit]
    response.update({"items": items})
    return response


#  ==================== 多层依赖项 ====================
def query_extractor(q: Union[str, None] = None):
    return q


def query_or_cookie_extractor(q: str = Depends(query_extractor), last_query: Union[str, None] = Cookie(default=None), ):
    if not q:
        return last_query
    return q


@router.get("/depend/items_6/", tags=["依赖项"])
async def read_query_6(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}


# FastAPI 不会为同一个请求多次调用同一个依赖项，而是把依赖项的返回值进行「缓存」，并把它传递给同一请求中所有需要使用该返回值的「依赖项」。
# 禁止缓存 ：把 Depends 的参数 use_cache 的值设置为 False
@router.get("/depend/items_7/", tags=["依赖项"])
async def needy_dependency_7(fresh_value: str = Depends(query_or_cookie_extractor, use_cache=False)):
    return {"fresh_value": fresh_value}


#  ==================== 路径操作装饰器依赖项 ====================
# 有时，我们并不需要在路径操作函数中使用依赖项的返回值。
# 或者说，有些依赖项不返回值。

# 无论路径装饰器依赖项是否返回值，路径操作都不会使用这些值。
async def verify_token(x_token: str = Header()):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: str = Header()):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@router.get("/depend/items_8/", dependencies=[Depends(verify_token), Depends(verify_key)], tags=["依赖项"])
async def read_items_8():
    return [{"item": "Foo"}, {"item": "Bar"}]


#  ==================== 全局依赖项 ====================
#  见 main.py
# 路径装饰器依赖项 一章的思路均适用于全局依赖项
async def verify_token(x_token: str = Header()):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: str = Header()):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


#  ==================== yield依赖项 ====================
# FastAPI支持在完成后执行一些额外步骤的依赖项.
# 确保在每个依赖中只使用一次 yield。
# 在发送响应之前，只会执行 yield 语句及之前的代码


# 有点类似于
async def yield_depend_9():
    print("--- 进入依赖项")
    yield 12
    print("--- 离开依赖项")


@router.get("/depend/items_9/", tags=["依赖项"])
async def read_items_9(q: int = Depends(yield_depend_9)):
    print("--- 进入路由,接受传递的值 ：", q)
    return q


async def yield_depend_10():
    try:
        print("--- 进入依赖项")
        yield 12
        print("--- 离开依赖项")
    except Exception as e:
        print('依赖项错误信息 ：')


@router.get("/depend/items_10/", tags=["依赖项"])
async def read_items_10(q: int = Depends(yield_depend_10)):
    print("--- 进入路由,接受传递的值 ：", q / 0)
    return q
